/**
 * 复数对象类
 * @param real 复数的实部，必须
 * @param imaginary 复数的虚部，必须
 * @param suffix  复数的后缀，i或j，必须。
 */
class Complex{
    constructor(real, imaginary, suffix){
        //确保两个实参都是数字
        if( !isNumber(real) || !isNumber(imaginary)){
            throw new TypeError();        //如果不都是数字则抛出错误
        }
        //检查后缀
        if(!(suffix === 'i' || suffix === 'j')){
            throw new TypeError();        //如果不是i或者j则抛出错误
        }
        this.r = real
        this.i = imaginary
        this.s = suffix || 'i'
    }
    /**
     * 将复数对象装换为一个字符串
     * @returns {string}
     */
    toString(){
        if(this.r ===0) {
            if (this.i === 0) {
                return '0'
            }
            if (this.i === 1) {
                return this.s
            }
            if (this.i > 1) {
                return this.i+this.s
            }
            if (this.i === -1) {
                return '-'+this.s
            }
            if (this.i < -1) {
                return this.i+this.s
            }

        } else{
            if( this.i === 0){
                return this.r
            }
            if( this.i === 1){
                return this.r+'+'+this.s
            }
            if( this.i === -1){
                this.i = '-'
                return this.r+'-'+this.s
            }
            if( this.i > 1){
                return this.r+'+'+this.i+this.s
            }
            if(this.i < -1){
                return this.r+''+this.i+this.s
            }
        }
    }
}

/**
 * 复数的操作对象：
 * 公有方法：
 * set:创建一个复数，返回值是一个Complex实例对象
 * sum:参数可以是复数，也可以是实数，返回两个或者多个复数（实数）之和，返回值是一个Complex实例对象。
 * sub:参数可以是复数，也可以是实数，返回两个复数（实数）只差，返回值是一个Complex实例对象。
 * mul:参数可以是复数，也可以是实数，返回两个或多个复数（实数）乘积，返回值是一个Complex实例对象。
 * div:参数可以是复数，也可以是实数，返回两个复数（实数）商，返回值是一个Complex实例对象。
 * con:参数必须是复数，返回复数的共轭复数。
 * abs:参数可以是复数，也可以是实数，返回复数（实数）的模，返回值是一个实数，Number类型。
 *
 * 工具方法：
 * is:判断一个对象是否是Complex实例对象,若是返回true，否则返回false
 */
const ComplexType={
    /**
     * 创建一个复数对象，返回值是complex实例对象类型
     * @param real 实部
     * @param imaginary 虚部
     * @param suffix 后缀i或者j
     * @returns {Complex}
     */
    set(real , imaginary, suffix){
        return new Complex(real , imaginary, suffix)
    },
    /**
     * @param complex
     * @returns {boolean}
     */
    is(complex){
        return complex.__proto__ === Complex.prototype
    },
    /**
     * 处理方法的参数（顺序保持），保证所有参数都是Complex对象
     * @param args 必须至少两个，可以多个 复数或者实数 复数或者实数
     * @returns {Array}
     */
    _handleParamter(...args){
        //保存所有复数对象
        let complexArr = []
        //复数后缀
        let s=''
        //参数必须至少两个，可以多个
        if(args.length < 2){
            throw new TypeError('Parameters must be at least two, and can be multiple complex or real')
        }
        //获取参数中复数的后缀
        args.forEach( item=>{
            if(this.is(item)){
                s += item.s
            }
        })
        //判断复数的后缀混，如果同时存在i和j，则抛出异常
        if(s.indexOf('i') !== -1 && s.indexOf('j') !== -1){
            throw Error('复数后缀必须统一，要么是i，要么是j ')
        }
        //重新设置复数后缀,如果参数都为实数，s设置为'i'，否则取第一位
        s = s.length?s.slice(0,1):'i'

        //将所有的复数追加到数组complexArr，如果是实数，则先将实数转为虚部为0的复数再进行
        args.forEach( item=>{
            //如果是complex对象，直接追加到数组complexArr
            if(this.is(item)){
                complexArr.push(item)
            }
            //如果是Number类型，先转为Complex对象，再追加到数组complexArr
            else if(isNumber(item)){
                complexArr.push(this.set(item,0,s))
            }else {//否则抛出异常
                throw new TypeError('The first  parameter is not allowed')
            }
        })
        return complexArr
    },
    /**
     * 返回复数的模，复数的模定义为原点（0,0）到复平面的距离。
     * @param complex
     * @returns {number}
     */
    abs(complex){
        if(isNumber(complex)){
            complex = this.set(complex,0,s)
        }else if(!isNumber(complex) && !this.is(complex)){//否则抛出异常
            throw new TypeError('Parameter must be complex or real')
        }
        return Math.sqrt(complex.r * complex.r + complex.i * complex.i);
    }
}

/***************工具方法**************************/
/**
 * 判断一个参数是否是数值类型
 * @param value
 * @returns {boolean}
 */
function isNumber(value){
    return typeof value === 'number' && !isNaN(value)
}





let a =  ComplexType.set(1,1,'j')
let b =  ComplexType.set(0,2,'j')
let c =  ComplexType.set(-3,4,'j')
let d =  ComplexType.set(-12,-5,'j')
let result = null

//测试|(1+j)|
result = ComplexType.abs(a)
console.log(a) // Complex { r: 1, i: 1, s: 'j' }
console.log('abs: ',result.toString(),'\n') //1.4142135623730951

//测试|(2j)|
result = ComplexType.abs(b)
console.log(b) // Complex { r: 0, i: 2, s: 'j' }
console.log('abs: ',result.toString(),'\n') //2

//测试|(-3+4j)|
result = ComplexType.abs(c)
console.log(c) // Complex { r: -3, i: 4, s: 'j' }
console.log('abs: ',result.toString(),'\n') //5

//测试|(-12+5j)|
result = ComplexType.abs(d)
console.log(c) // Complex { r: -12, i: -5, s: 'j' }
console.log('abs: ',result.toString(),'\n') //  abs:  13

console.log(Object.prototype.toString.call(result)) // [object Number]